//===--- TaskQueue.cpp - Task Execution Work Queue ------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file includes the appropriate platform-specific TaskQueue
/// implementation (or the default serial fallback if one is not available),
/// as well as any platform-agnostic TaskQueue functionality.
///
//===----------------------------------------------------------------------===//

#include "polarphp/basic/TaskQueue.h"

using namespace polar;
using namespace polar::sys;

// Include the correct TaskQueue implementation.
// Include the correct TaskQueue implementation.
#if LLVM_ON_UNIX && !defined(__CYGWIN__) && !defined(__HAIKU__)
#include "polarphp/basic/internal/_platform/TaskQueueImplUnix.h"
#else
#include "polarphp/basic/internal/_platform/TaskQueueImplDefault.h"
#endif

namespace polar {
namespace sys {
void TaskProcessInformation::ResourceUsage::provideMapping(json::Output &out) {
   out.mapRequired("utime", Utime);
   out.mapRequired("stime", Stime);
   out.mapRequired("maxrss", Maxrss);
}

void TaskProcessInformation::provideMapping(json::Output &out) {
   out.mapRequired("real_pid", OSPid);
   if (ProcessUsage.hasValue())
      out.mapRequired("usage", ProcessUsage.getValue());
}
}
}

TaskQueue::TaskQueue(unsigned NumberOfParallelTasks,
                     UnifiedStatsReporter *USR)
   : NumberOfParallelTasks(NumberOfParallelTasks),
     Stats(USR){}

TaskQueue::~TaskQueue() = default;

// DummyTaskQueue implementation

DummyTaskQueue::DummyTaskQueue(unsigned NumberOfParallelTasks)
   : TaskQueue(NumberOfParallelTasks) {}

DummyTaskQueue::~DummyTaskQueue() = default;

void DummyTaskQueue::addTask(const char *ExecPath, ArrayRef<const char *> Args,
                             ArrayRef<const char *> Env, void *Context,
                             bool SeparateErrors) {
   QueuedTasks.emplace(std::unique_ptr<DummyTask>(
      new DummyTask(ExecPath, Args, Env, Context, SeparateErrors)));
}

bool DummyTaskQueue::execute(TaskQueue::TaskBeganCallback Began,
                             TaskQueue::TaskFinishedCallback Finished,
                             TaskQueue::TaskSignalledCallback Signalled) {
   using PidTaskPair = std::pair<ProcessId, std::unique_ptr<DummyTask>>;
   std::queue<PidTaskPair> ExecutingTasks;

   bool SubtaskFailed = false;

   static ProcessId Pid = 0;

   unsigned MaxNumberOfParallelTasks = getNumberOfParallelTasks();

   while ((!QueuedTasks.empty() && !SubtaskFailed) ||
          !ExecutingTasks.empty()) {
      // Enqueue additional tasks if we have additional tasks, we aren't already
      // at the parallel limit, and no earlier subtasks have failed.
      while (!SubtaskFailed && !QueuedTasks.empty() &&
             ExecutingTasks.size() < MaxNumberOfParallelTasks) {
         std::unique_ptr<DummyTask> T(QueuedTasks.front().release());
         QueuedTasks.pop();

         if (Began)
            Began(++Pid, T->Context);

         ExecutingTasks.push(PidTaskPair(Pid, std::move(T)));
      }

      // Finish the first scheduled task.
      PidTaskPair P = std::move(ExecutingTasks.front());
      ExecutingTasks.pop();

      if (Finished) {
         std::string Output = "Output placeholder\n";
         std::string Errors =
            P.second->SeparateErrors ? "Error placeholder\n" : "";
         if (Finished(P.first, 0, Output, Errors, TaskProcessInformation(Pid),
                      P.second->Context) == TaskFinishedResponse::StopExecution)
            SubtaskFailed = true;
      }
   }

   return false;
}
